using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Threading;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Proxies;

namespace PluginManager
{
    /// <summary>
    /// A class that acts as a midway between the PluginManager and the 
    /// RemoteClassLoader
    /// </summary>
    public class LocalClassLoader : MarshalByRefObject
    {
        AppDomain appDomain;
        RemoteClassLoader remoteLoader;
        /// <summary>
        /// Creates a new LocalClassLoader
        /// </summary>
        /// <param name="pluginDirectory">The directory where the plugins are located</param>
        public LocalClassLoader(string pluginDirectory)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationName = "Plugins";
            setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
            setup.PrivateBinPath = Path.GetDirectoryName(pluginDirectory).Substring(Path.GetDirectoryName(pluginDirectory).LastIndexOf(Path.DirectorySeparatorChar) + 1);
            setup.CachePath = Path.Combine(pluginDirectory, "cache" + Path.DirectorySeparatorChar);
            setup.ShadowCopyFiles = "true";
            setup.ShadowCopyDirectories = pluginDirectory;

            appDomain = AppDomain.CreateDomain("Plugins", null, setup);
            remoteLoader = (RemoteClassLoader)appDomain.CreateInstanceAndUnwrap("PluginManager", "PluginManager.RemoteClassLoader");
        }
        /// <summary>
        /// Loads the assembly into the <see cref="RemoteClassLoader"/>
        /// </summary>
        /// <param name="filename">The Full filename of the Assembly</param>
        public void LoadAssembly(string filename)
        {
            try
            {
                remoteLoader.LoadAssembly(filename);
            }
            catch (BadImageFormatException) { }
            catch (FileNotFoundException) { }
            catch (FileLoadException) { }
        }
        /// <summary>
        /// Destroys the AppDomain where the <see cref="RemoteClassLoader"/>
        /// resides
        /// </summary>
        public void Unload()
        {
            if (appDomain != null)
                AppDomain.Unload(appDomain);
            appDomain = null;
        }

        /// <summary>
        /// A string array of all of the Assemblies handled by this ClassLoader
        /// </summary>
        public string[] Assemblies
        {
            get
            {
                return remoteLoader.GetAssemblies();
            }
        }

        /// <summary>
        /// Gets all of the Attributes of A type
        /// </summary>
        /// <param name="typeName">The FullName of the type whose Attributes you want</param>
        /// <returns>An object Array of all the attributes</returns>
        public object[] GetAttributes(string typeName)
        {
            return remoteLoader.GetAttributes(typeName);
        }

        /// <summary>
        /// A string array of all of the <see cref="CoreEventListener"/>s 
        /// handled by this ClassLoader
        /// </summary>
        public string[] Listeners
        {
            get
            {
                return remoteLoader.GetListeners();
            }
        }

        /// <summary>
        /// Checks if <paramref name="typeName"/>is part of this assembly
        /// </summary>
        /// <param name="typeName">The TypeName to look for</param>
        /// <returns>Returns True if <paramref name="typeName"/> belongs to any of the assemblies loaded</returns>
        public bool ManagesType(string typeName)
        {
            return remoteLoader.ManagesType(typeName);
        }

        /// <summary>
        /// Creates an Instance of the <paramref name="typeName"/>
        /// </summary>
        /// <param name="typeName">The FullName of the type to be created</param>
        /// <returns>A new instance of the class</returns>
        public object CreateInstance(string typeName)
        {
            string file;

            file = remoteLoader.GetOwningAssembly(typeName);
            file = Path.Combine(PluginManager.pluginDirectory, file);

                ObjectHandle oh = Activator.CreateInstanceFrom(file, typeName);
                return oh.Unwrap();
            

        }
    }
}
